Lær hvordan du implementerer React Error Boundaries med hooks for å håndtere ressurslastingsfeil, noe som forbedrer brukeropplevelsen og applikasjonsstabiliteten.
Robust ressurslasting i React: Mestring av Error Boundaries med Hooks
I moderne webapplikasjoner er asynkron lasting av ressurser vanlig praksis. Enten det er henting av data fra et API, lasting av bilder eller import av moduler, er håndtering av potensielle feil under ressurslasting avgjørende for en god brukeropplevelse. React Error Boundaries gir en mekanisme for å fange JavaScript-feil hvor som helst i deres barn-komponenttre, logge disse feilene og vise et reserve-UI (fallback UI) i stedet for å krasje hele applikasjonen. Denne artikkelen utforsker hvordan man effektivt kan bruke Error Boundaries i kombinasjon med React Hooks for å håndtere ressurslastingsfeil.
Forståelse av Error Boundaries
Før React 16 kunne uhåndterte JavaScript-feil under komponent-rendering ødelegge Reacts interne tilstand og forårsake kryptiske feil ved senere renderinger. Error Boundaries løser dette ved å fungere som en «catch-all»-blokk for feil som oppstår i deres barnekomponenter. De er React-komponenter som implementerer en eller begge av følgende livssyklusmetoder:
static getDerivedStateFromError(error): Denne statiske metoden påkalles etter at en feil er kastet av en etterfølgende komponent. Den mottar feilen som ble kastet som et argument og returnerer en verdi for å oppdatere komponentens tilstand.componentDidCatch(error, info): Denne livssyklusmetoden påkalles etter at en feil er kastet av en etterfølgende komponent. Den mottar feilen som ble kastet som et argument, samt et objekt som inneholder informasjon om hvilken komponent som kastet feilen. Du kan bruke den til å logge feilinformasjon.
Viktigst er at Error Boundaries kun fanger feil i renderingsfasen, i livssyklusmetoder og i konstruktører for hele treet under dem. De fanger ikke feil for:
- Hendelseshåndterere (lær mer i avsnittet nedenfor)
- Asynkron kode (f.eks.
setTimeoutellerrequestAnimationFramecallbacks) - Server-side rendering
- Feil som kastes i selve Error Boundary-komponenten (i stedet for i barna)
Error Boundaries og React Hooks: En kraftig kombinasjon
Mens klassekomponenter tradisjonelt ble brukt til å implementere Error Boundaries, tilbyr React Hooks en mer konsis og funksjonell tilnærming. Vi kan lage en gjenbrukbar useErrorBoundary-hook som innkapsler feilhåndteringslogikken og gir en praktisk måte å pakke inn komponenter som kan kaste feil under ressurslasting.
Opprette en tilpasset useErrorBoundary-hook
Her er et eksempel på en useErrorBoundary-hook:
import { useState, useCallback } from 'react';
function useErrorBoundary() {
const [error, setError] = useState(null);
const resetError = useCallback(() => {
setError(null);
}, []);
const captureError = useCallback((e) => {
setError(e);
}, []);
const ErrorBoundary = useCallback(({ children, fallback }) => {
if (error) {
return fallback ? fallback : An error occurred: {error.message || String(error)};
}
return children;
}, [error]);
return { ErrorBoundary, captureError, error, resetError };
}
export default useErrorBoundary;
Forklaring:
useState: Vi brukeruseStatetil å håndtere feiltilstanden. Den setter i utgangspunktet feilen tilnull.useCallback: Vi brukeruseCallbacktil å memoize funksjoneneresetErrorogcaptureError. Dette er god praksis for å forhindre unødvendige re-rendringer hvis disse funksjonene sendes ned som props.ErrorBoundary-komponent: Dette er en funksjonell komponent laget meduseCallbacksom tar imotchildrenog en valgfrifallback-prop. Hvis det finnes en feil i tilstanden, rendrer den enten den angittefallback-komponenten eller en standard feilmelding. Ellers rendrer den barna. Dette fungerer som vår Error Boundary. Avhengighetsarrayet `[error]` sikrer at den re-rendres nårerror-tilstanden endres.captureError-funksjon: Denne funksjonen brukes til å sette feiltilstanden. Du vil kalle denne inne i entry...catch-blokk når du laster ressurser.resetError-funksjon: Denne funksjonen tømmer feiltilstanden, slik at komponenten kan re-rendre barna sine (og potensielt forsøke ressurslastingen på nytt).
Implementere ressurslasting med feilhåndtering
La oss nå se hvordan vi bruker denne hooken til å håndtere ressurslastingsfeil. Tenk deg en komponent som henter brukerdata fra et API:
import React, { useState, useEffect } from 'react';
import useErrorBoundary from './useErrorBoundary';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const { ErrorBoundary, captureError, error, resetError } = useErrorBoundary();
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (e) {
captureError(e);
}
};
fetchData();
}, [userId, captureError]);
if (error) {
return (
Failed to load user data. {user.name}
Email: {user.email}
{/* Other user details */}Forklaring:
- Vi importerer
useErrorBoundary-hooken. - Vi kaller hooken for å få
ErrorBoundary-komponenten,captureError-funksjonen,error-tilstanden ogresetError-funksjonen. - Inne i
useEffect-hooken pakker vi API-kallet inn i entry...catch-blokk. - Hvis en feil oppstår under API-kallet, kaller vi
captureError(e)for å sette feiltilstanden. - Hvis
error-tilstanden er satt, rendrer viErrorBoundary-komponenten. Vi gir en tilpassetfallback-prop som viser en feilmelding og en "Prøv igjen"-knapp. Et klikk på knappen kallerresetErrorfor å tømme feiltilstanden, noe som utløser en re-rendering og et nytt forsøk på å hente dataene. - Hvis ingen feil oppstod og brukerdataene er lastet, rendrer vi brukerprofil-detaljene.
Håndtering av ulike typer ressurslastingsfeil
Ulike typer ressurslastingsfeil kan kreve forskjellige håndteringsstrategier. Her er noen vanlige scenarioer og hvordan man kan håndtere dem:
Nettverksfeil
Nettverksfeil oppstår når klienten ikke klarer å koble til serveren (f.eks. på grunn av et nettverksbrudd eller nedetid på serveren). Eksemplet ovenfor håndterer allerede grunnleggende nettverksfeil ved hjelp av `response.ok`. Du vil kanskje legge til mer sofistikert feiloppdagelse, for eksempel:
// Inne i fetchData-funksjonen
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
// Vurder å legge til spesifikk feilkodehåndtering
if (response.status === 404) {
throw new Error("User not found");
} else if (response.status >= 500) {
throw new Error("Server error. Please try again later.");
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error.message === 'Failed to fetch') {
// Sannsynligvis en nettverksfeil
captureError(new Error('Network error. Please check your internet connection.'));
} else {
captureError(error);
}
}
I dette tilfellet kan du vise en melding til brukeren som indikerer at det er et problem med nettverkstilkoblingen og foreslå at de sjekker internettforbindelsen sin.
API-feil
API-feil oppstår når serveren returnerer en feilrespons (f.eks. en 400 Bad Request eller en 500 Internal Server Error). Som vist ovenfor, kan du sjekke `response.status` og håndtere disse feilene på en passende måte.
Dataparsingsfeil
Dataparsingsfeil oppstår når responsen fra serveren ikke er i forventet format og ikke kan tolkes (f.eks. ugyldig JSON). Du kan håndtere disse feilene ved å pakke response.json()-kallet inn i en try...catch-blokk:
// Inne i fetchData-funksjonen
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error instanceof SyntaxError) {
captureError(new Error('Failed to parse data from server.'));
} else {
captureError(error);
}
}
Bildeinnlastingsfeil
For bildeinnlasting kan du bruke onError-hendelseshåndtereren på <img>-taggen:
function MyImage({ src, alt }) {
const { ErrorBoundary, captureError } = useErrorBoundary();
const [imageLoaded, setImageLoaded] = useState(false);
const handleImageLoad = () => {
setImageLoaded(true);
};
const handleImageError = (e) => {
captureError(new Error(`Failed to load image: ${src}`));
};
return (
Failed to load image.